/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package app.algo;

import app.utils.ImageUtils;
import app.utils.SiftLogger;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.awt.image.renderable.ParameterBlock;
import java.util.logging.Level;
//import javax.media.jai.JAI;


/**
 *
 * @author bharat
 */
public class GaussianScaleSpacePyramidBuilder {

    private BufferedImage initializeGaussianBuilder(BufferedImage img, boolean shouldDouble, double initSigma){
        BufferedImage initImg = null ;
        float diffGauss = 0.0f ;
        Kernel kernel = null ;
        ConvolveOp cop = null ;


        BufferedImage grayScaleImg = ImageUtils.getGrayScaleImage(img) ;
        if (shouldDouble){
            diffGauss = (float) Math.sqrt(initSigma * initSigma - SiftSettings.SIFT_INIT_SIGMA * SiftSettings.SIFT_INIT_SIGMA * 4) ;
            grayScaleImg = ImageUtils.scaleImage(grayScaleImg, grayScaleImg.getWidth()*2, grayScaleImg.getHeight()*2) ;
        }else{
            diffGauss = (float) Math.sqrt(initSigma * initSigma - SiftSettings.SIFT_INIT_SIGMA * SiftSettings.SIFT_INIT_SIGMA ) ;
        }

        initImg = new BufferedImage(grayScaleImg.getWidth(), grayScaleImg.getHeight(), BufferedImage.TYPE_INT_ARGB);

        kernel = ImageUtils.makeGaussinKernel1D( diffGauss ) ;
        cop = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
        cop.filter(grayScaleImg, initImg) ;

        int pix ;
        for (int i=0; i < initImg.getWidth(); i++){
            for (int j =0 ; j < initImg.getHeight(); j++){
                pix = initImg.getRGB(i, j) ;
                System.out.println("Pix : " + pix + ", Normal: " +  (1.0*pix/Integer.MAX_VALUE));
            }
        }
                    
        return initImg ;
    }

    public BufferedImage[][] buildGaussianPyramid(BufferedImage sampleImg, int intervals, double sigma){

        double sig_total, sig_prev, k;
        BufferedImage[][] gaussImgs = null ;

        /* initialization step */
        BufferedImage img =  initializeGaussianBuilder(sampleImg, SiftSettings.SIFT_IMAGE_DOUBLE, sigma);
        int octave =  (int) ((Math.log(Math.min(img.getWidth(), img.getHeight())) / Math.log(2)) - 2);


        // need to have atlest 3 more sigma level of convolution
        float[] sig = new float[intervals + 3] ;
        gaussImgs = new BufferedImage[octave][intervals + 3] ;

        /* Precomputing the Sigma Vales */
        sig[0] = (float) sigma;
        k = Math.pow(2.0, 1.0 / intervals);

        SiftLogger.log(Level.INFO,  "Octaves: " + octave + ", Interval : " + intervals + ", InitSigma=" + sigma + ", K = " + k );

        for (int i = 1; i < intervals + 3; i++) {
            sig_prev = Math.pow(k, i - 1) * sigma;
            sig_total = sig_prev * k;
            sig[i] = (float) Math.sqrt(sig_total * sig_total - sig_prev * sig_prev);
            SiftLogger.log(Level.INFO,  "Sigma-" + i + ": " + sig[i]);
        }

        Kernel kernel = null ;
        ConvolveOp cop = null ;

        for (int o = 0; o < octave; o++) {
            for (int i = 0; i < intervals + 3; i++) {
                if (o == 0 && i == 0) {
                    gaussImgs[o][i] = img;
                }

                /* base of new octvave is halved image from end of previous octave */
                else if (i == 0) {
                    gaussImgs[o][i] = ImageUtils.scaleImage(gaussImgs[o-1][i], gaussImgs[o-1][i].getWidth()/2, gaussImgs[o-1][i].getHeight()/2) ;
                }

                /* blur the current octave's last image to create the next one */
                else {
                    gaussImgs[o][i] = new BufferedImage (gaussImgs[o][i-1].getWidth(), gaussImgs[o][i-1].getHeight(), BufferedImage.TYPE_INT_ARGB)  ;
                    kernel = ImageUtils.makeGaussinKernel1D( sig [i] ) ;
                    cop = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
                    cop.filter(gaussImgs[o][i-1] , gaussImgs[o][i]) ;
                    
                }
            }
        }
        return gaussImgs;
    }

    private void calcDOG(BufferedImage src1, BufferedImage src2, BufferedImage diff){

        /*
        ParameterBlock pb = new ParameterBlock();
		pb.addSource(img1);
		pb.addSource(img2);

		return JAI.create("subtract", pb);
    */

	int pix,pix1,pix2,r1,g1,b1, a1,r2,g2,b2,a2, r,g,b,a;
	for(int i = 0;  i < diff.getHeight(); i++){
	    for(int j = 0; j < diff.getWidth(); j++){
		
		pix1 = src1.getRGB(j, i);
		pix2 = src2.getRGB(j, i);

                a1=(pix1 >> 32) & 0xff;
		r1=(pix1 >> 16) & 0xff;
		g1=(pix1 >> 8) & 0xff;
		b1=pix1 & 0xff;

                a2=(pix2 >> 32) & 0xff;
		r2=(pix2 >> 16) & 0xff;
		g2=(pix2 >> 8) & 0xff;
		b2=pix2 & 0xff;

                a = ((a1-a2)<0)?(0):(((a1-a2)>255)?255:(a1-a2));
		r = ((r1-r2)<0)?(0):(((r1-r2)>255)?255:(r1-r2));
		g = ((g1-g2)<0)?(0):(((g1-g2)>255)?255:(g1-g2));
		b = ((b1-b2)<0)?(0):(((b1-b2)>255)?255:(b1-b2));

		pix = ((0x00000000) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff));
                
		diff.setRGB(j, i, pix);
	    }
	}
    }

    public BufferedImage[] buildDogPyramid(BufferedImage[] guassianOctaveInts, int octave, int intervals){

	BufferedImage[] DOGPyramid = new BufferedImage[intervals+2];

	    for(int i = 0; i < intervals + 2; i++){
		DOGPyramid[i] = new BufferedImage(guassianOctaveInts[i].getWidth(), guassianOctaveInts[i].getHeight(), BufferedImage.TYPE_INT_ARGB);
		calcDOG(guassianOctaveInts[i+1],guassianOctaveInts[i], DOGPyramid[i]);
	    }
	return DOGPyramid;
    }
    
}
